home *** CD-ROM | disk | FTP | other *** search
/ The Atari Compendium / The Atari Compendium (Toad Computers) (1994).iso / files / prgtools / mint / utilit~1 / stcron3.lzh / CROND.C < prev    next >
Encoding:
C/C++ Source or Header  |  1993-10-03  |  10.8 KB  |  420 lines

  1. /* CROND: (c) Kees Lemmens, Netherlands; June 1993.
  2.  
  3.     Program for ATARI ST (running under MINT) to make it possible
  4.     to run background jobs at regular intervals.
  5.  
  6.     This is the daemon itself. It forks automatically when started
  7.     and then tests every minute if one of the jobs in the joblist
  8.     should be run.
  9.     The joblist is only builded when the daemon starts and when it 
  10.     receives a signal from another process. (i.e. CRONTAB or AT)
  11.     
  12.     A pipe is used to supply commands to the daemon. This pipe also
  13.     functions as a lock and thus prevents a second daemon to be
  14.     started by accident.
  15.     
  16.     I choose to use a bitmask on the job schedule times, and I can
  17.     tell you it took me a hell lot of time to get a good idea about
  18.     how to do it !
  19.  
  20.     Version 2.1 :
  21.  
  22.     - Now cron also takes care of the right user and group id's for
  23.       the spawned processes.
  24.       Also AT and CRONTAB use the Pgetuid() function after the
  25.       environment variable LOGNAME to determine the username.
  26.       However the username can also be supplied with the -u option or
  27.       by redefinition of LOGNAME : So you see this isn't very secure !
  28.     
  29.       But after all, MiNT is not supposed to be a secure system.
  30.  
  31.     - Before the job starts the working directory is now changed to
  32.       the one specified in the passwd file.
  33.  
  34.     - I replaced the old SIGHUP mechamism with a COMMANDPIPE, because
  35.       users with non-zero uid's aren't allowed to give signals to the
  36.       crondaemon and I didn't see another way to change this.
  37.     
  38.     Any questions or suggestions about this program can be send to:
  39.     lemmens@dv.twi.tudelft.nl
  40. */
  41.  
  42. #include "cron.h"
  43.  
  44. #include <stdio.h>
  45. #include <string.h>
  46. #include <stdlib.h>
  47. #include <time.h>
  48. #include <ctype.h>
  49. #include <sys\wait.h>
  50. #include <errno.h>
  51. #include <signal.h>    
  52.  
  53. /* __MINT__ must be predefined or else we have wrong signals ! */
  54.  
  55. static int cronpipe;
  56.  
  57. extern void    exit(int);
  58. extern void cronsleep(unsigned long);
  59. extern char *ux2dos(char *string);
  60.  
  61. extern void DoJobs(entry joblist[],int jobcount,active ajoblist[]);
  62. extern void HandleExits(active actjoblist[]);
  63. extern void LogMsg(char *name, int pid, char *fmt,...);
  64. extern void PokeDaemon(int cmd);
  65.  
  66. /*    CreatePipe() - write our PID into /etc/crond.pid and create
  67.     the command pipe (unless another daemon is already running).
  68. */
  69.  
  70. void CreatePipe(void)
  71. {    char spid[10];
  72.     int pidfile;
  73.  
  74.     if ((cronpipe = open(ux2dos(CRONPIPE),O_RDWR|O_CREAT)) < 0)
  75.     {    fprintf(stderr,PROGNAME": Can't create command pipe !\n");    
  76.         exit(1);
  77.     }
  78.  
  79.     if ((pidfile = open(ux2dos(PIDFILE),O_WRONLY|O_CREAT)) < 0)
  80.     {    fprintf(stderr,PROGNAME": Can't open %s\n",PIDFILE);
  81.         exit(1);
  82.     }
  83.     sprintf(spid, "%.4d\n", getpid());
  84.     write(pidfile,spid,strlen(spid));
  85.     close(pidfile);
  86. }
  87.  
  88. void DaemonMode(void)
  89. {
  90. #ifndef DEBUG
  91.     switch ((int)fork())
  92.     {    case -1:
  93.             fprintf(stderr,"%s: can't fork daemon !\n",PROGNAME);
  94.             exit(1);
  95.             break;
  96.         case 0:
  97.             /* start child process */
  98.             (void)setpgrp(0,0);
  99.             (void)kill(getppid(),SIGKILL);    /* kill parent */
  100.             (void)nice(NICELEVEL);    /* don't disturb others */
  101.             LogMsg(PROGNAME,getpid(),"Cron daemon started");
  102.             break;
  103.         default:
  104.             /* parent process should wait to be killed */
  105.             wait();
  106.             break;
  107.     }
  108. #endif
  109. }
  110.  
  111. int CalcStartTimes(entry joblist[],int jobcount,time_t starttm)
  112. {    int x,runjobs=0;
  113.     struct tm *ti;
  114.     entry msk;
  115.  
  116.     ti = localtime(&starttm);
  117.  
  118.     msk.MinH = (ti->tm_min <  32) ? 0L:1L<<(ti->tm_min - 32);
  119.     msk.MinL = (ti->tm_min >= 32) ? 0L:1L<<ti->tm_min;    
  120.  
  121.     msk.Hour   = 1L<<ti->tm_hour;
  122.     msk.DayOfM = 1L<<ti->tm_mday;
  123.     msk.Month  = 1<<(ti->tm_mon+1); /* must be from 1-12 */
  124.     msk.DayOfW = 1<<ti->tm_wday;
  125.  
  126. #ifdef DEBUG
  127.     Debug("Current time mask :");
  128.     DebugPrintTimes(&msk);
  129. #endif    
  130.  
  131.     for(x=0;x<jobcount;x++)
  132.     {    if
  133.         (      ((msk.MinH   & joblist[x].MinH  ) /* must be <OR> ! */
  134.             || (msk.MinL   & joblist[x].MinL  ))
  135.             && (msk.Hour   & joblist[x].Hour  )
  136.             && (msk.DayOfM & joblist[x].DayOfM)
  137.             && (msk.Month  & joblist[x].Month )
  138.             && (msk.DayOfW & joblist[x].DayOfW)
  139.         )
  140.         {    joblist[x].Status = START;
  141.             runjobs++;
  142. #ifdef DEBUG
  143.             Debug("CalcStartTimes: job %d will start next run\n",x);
  144. #endif
  145.         }
  146.         else
  147.             joblist[x].Status = SLEEP;
  148.     }
  149.     return runjobs;
  150. }
  151.  
  152. /* next routine is both used for minutes ( 2 seperate fields, because
  153.    32 bits isn't enough ) as for hours, day of month, month and day of 
  154.    week that can do with one field.
  155.  
  156.    This implies we always have to supply two variables : a low part 
  157.    and a high part. The high part is only set if the number of bits
  158.    is greater than 32, so only for the minutes we have to supply a
  159.    valid pointer for the high part.
  160.  
  161.    This saves the need of two separate functions and doesn't make the
  162.    code much more complicated. (to my opinion !)
  163. */   
  164.    
  165. int FillTimeMask(ULONG *maskH,ULONG *maskL,char *string,int nrbits)
  166. {    long tmpmaskH = 0, tmpmaskL = 0L;
  167.     char *tmp;
  168.     int start,end,shift;
  169.  
  170.     *maskL = 0L;
  171.     if(nrbits > 32) *maskH = 0L;
  172.  
  173.     /* test for * which means set all bits */
  174.     
  175.     if (*string == '*')
  176.     {    *maskL = -1L;
  177.         if(nrbits > 32) *maskH = -1L;
  178.         return 0;
  179.     }
  180.  
  181.     /* search for intervals ( - ) */
  182.     
  183.     if(strchr(string,'-') != NULL)
  184.     {    sscanf(string,"%d-%d",&start,&end);
  185.         if(start > end)
  186.         {    LogMsg(PROGNAME,getpid(),"Illegal interval %d-%d: ignored",
  187.                 start,end);
  188.             return -1;
  189.         }
  190.         if(start > nrbits || end > nrbits)
  191.         {    LogMsg(PROGNAME,getpid(),"Interval overflow %d-%d: corrected",
  192.                 start,end);
  193.             if (end > nrbits)
  194.                 end = nrbits;
  195.             if(start > nrbits)
  196.                 return -1;
  197.         }
  198.         if(end >= 32 && nrbits > 32)
  199.         {    shift = start < 32 ? 0 : start-32;
  200.             tmpmaskH = (1L<<(end - 32 + 1 - shift )) - 1L;
  201.             *maskH = tmpmaskH<<shift;
  202.         }
  203.         if(start < 32)
  204.         {    tmpmaskL = (1L<<( (end > 32 ? 32 : end) + 1 - start)) - 1L;
  205.             *maskL = tmpmaskL<<start;
  206.         }
  207.  
  208.         return 0;
  209.     }
  210.  
  211.     /* do single fields and also search for comma's (,) */
  212.  
  213.     tmp = string;
  214.     do
  215.     {    if(*tmp == ',')
  216.             string = tmp + 1;
  217.  
  218.         start = atoi(string);
  219.         if(start > nrbits)
  220.         {    LogMsg(PROGNAME,getpid(),"Field overflow %d: ignored",
  221.                 start);
  222.             continue;
  223.         }
  224.         if(start < 32)
  225.             *maskL |= 1L<<start;
  226.         else if(nrbits > 32)
  227.             *maskH |= 1L<<(start - 32);
  228.     }while((tmp=strchr(string,',')) != NULL);
  229.  
  230.     return 0;
  231. }
  232.  
  233. int ReadEntry(FILE *cronfile, entry *line)
  234. {    char *tmp,*ptr,buf[MAX_TEMPSTR];
  235.     char *number[5];
  236.     ULONG tmpfield;
  237.     int  x;
  238.  
  239.     ptr=fgets(buf,MAX_TEMPSTR - 1, cronfile);
  240.  
  241.     if(feof(cronfile)) return -1;
  242.     
  243.     for(x=0;x<5;x++)
  244.     {    while (*ptr=='\t' || *ptr==' ')    ptr++;
  245.         number[x] = ptr;
  246.         while (*ptr!='\t' && *ptr!=' ' &&
  247.                *ptr!='\n' && *ptr!='\0') ptr++;
  248.         *ptr++ ='\0';
  249.     }
  250.     while (*ptr=='\t' || *ptr==' ')    ptr++;
  251.  
  252.     if((tmp=strchr(ptr,'\n')) != NULL) *tmp = '\0';
  253.     strncpy(line->Command,ptr,MAX_COMMAND - 1);
  254.     
  255.     FillTimeMask(&line->MinH  ,&line->MinL  ,number[0],MAX_MIN);
  256.     FillTimeMask((ULONG *)NULL,&line->Hour  ,number[1],MAX_HOUR);
  257.     FillTimeMask((ULONG *)NULL,&line->DayOfM,number[2],MAX_DAYOFM);
  258.  
  259.     FillTimeMask((ULONG *)NULL,&tmpfield ,number[3],MAX_MONTH);
  260.     line->Month = (UINT)tmpfield;
  261.     
  262.     FillTimeMask((ULONG *)NULL,&tmpfield ,number[4],MAX_DAYOFW);
  263.     line->DayOfW = (UCHAR)tmpfield;
  264.  
  265.     return 0;
  266. }
  267.  
  268. int RebuildDir(char *dirname,char jobtype,int jobcount,entry joblist[])
  269. {    char cronname[MAX_FNAME];
  270.     long DirHandle;
  271.     struct dirent d_ent;
  272.     char *fileid;
  273.     FILE *cronfile;
  274.  
  275.     if((DirHandle=opendir(ux2dos(dirname))) < 0)
  276.     {    fprintf(stderr,"Directory %s doesn't exist !\n",dirname);
  277.         exit(1);
  278.     }    
  279.  
  280.     /* Rem. : Dreaddir is MINT specific call */
  281.  
  282.     while(Dreaddir(sizeof(d_ent),DirHandle,&d_ent.d_ino) == 0L)
  283.     {    if (*d_ent.d_name == '.')
  284.             continue;    /* skip . and .. */
  285.  
  286.         sprintf(cronname,"%s/%s",dirname,d_ent.d_name);
  287.  
  288.         if((cronfile = fopen(ux2dos(cronname),"r")) == NULL)
  289.         {    LogMsg(PROGNAME, getpid(), "Read failed for %s",cronname);
  290.             continue;
  291.         }    
  292.  
  293.         while(jobcount < MAX_JOBS)
  294.         {    int x;
  295.         
  296.             if(ReadEntry(cronfile,&joblist[jobcount]) < 0)
  297.                 break;
  298.  
  299.             if((fileid = strchr(d_ent.d_name,'.')) != NULL)
  300.             {    *fileid++ = '\0';    /* separate name and extension */
  301.                 joblist[jobcount].AtId = atoi(fileid);
  302.             }
  303.  
  304.             for(x=0;x<MAX_UNAME -1 && d_ent.d_name[x];x++)
  305.                 joblist[jobcount].User[x] = tolower(d_ent.d_name[x]);
  306.  
  307.             joblist[jobcount].User[x] = '\0';
  308.             
  309.             /* User must be lowercase to use pwgetnam() function */
  310.             
  311.             joblist[jobcount].Type = jobtype;
  312.  
  313. #ifdef DEBUG
  314.             Debug("RebuildTables: Command %d =\"%s\" for %s added\n",
  315.                 jobcount,joblist[jobcount].Command,
  316.                 joblist[jobcount].User);
  317.  
  318.             if(jobtype == AT)
  319.                 Debug("AT Job id : %03d\n",joblist[jobcount].AtId);
  320.                 
  321.             Debug("Job %d (type %d) will run at :",jobcount,jobtype);
  322.             DebugPrintTimes(&joblist[jobcount]);
  323. #endif
  324.             jobcount++;
  325.         }
  326.         if(jobcount >= MAX_JOBS)
  327.             LogMsg(PROGNAME,getpid(),"No more jobs, table full !");
  328.  
  329.         fclose(cronfile);
  330.     }
  331.     closedir(DirHandle);
  332.  
  333.     return jobcount;
  334. }
  335.  
  336. int RebuildTables(entry joblist[])
  337. {    int jobcount;
  338.  
  339.     jobcount = RebuildDir(CRONDIR,CRON,0,joblist);
  340.     jobcount = RebuildDir(ATDIR,AT,jobcount,joblist);
  341.  
  342.     return jobcount;
  343. }
  344.  
  345. void SigIntHandler(void)
  346. {    close(cronpipe);
  347.     LogMsg(PROGNAME,getpid(),"Terminate signal received");
  348.     remove(PIDFILE);    /* remove pidfile */
  349.     exit(0);
  350. }
  351.  
  352. void main(void)
  353. {    time_t currenttime,starttime;
  354.     char cmd;
  355.     long interval;
  356.     int jobcount,runjobs;
  357.     static entry joblist[MAX_JOBS];
  358.     static active ajoblist[MAX_SIMJOBS];
  359.  
  360. /* Fixed number of jobs and active jobs to avoid system overload */
  361. /* In active joblist pids from running jobs are saved until finished */
  362.  
  363.     if(getuid() != 0)
  364.     {    fprintf(stderr,"Must have uid 0 !\n");
  365.         exit(1);
  366.     }
  367.     DaemonMode();
  368.     CreatePipe();
  369.  
  370. #ifndef DEBUG
  371.     signal(SIGINT,SigIntHandler);
  372.     signal(SIGTERM,SigIntHandler);
  373.     signal(SIGHUP,SIG_IGN);
  374.     signal(SIGCHLD,SIG_IGN);
  375.  
  376.     sigblock((1L<<SIGINT) | (1L<<SIGTERM));
  377.      /* block SIGINT and SIGTERM : only handled in sleep */
  378. #endif
  379.  
  380.     PokeDaemon(INTERNBUILD);
  381.     
  382.     while (TRUE)
  383.     {    
  384. #ifndef DEBUG
  385.         starttime = ((time(¤ttime) / 60L) +1) * 60L;
  386. #else
  387.         starttime = currenttime;    /* faster testing ! */
  388.         Debug("Next run at %.24s with %d jobs\n",ctime(&starttime),activjobs);
  389. #endif
  390.         do
  391.         {
  392.             if(Finstat(cronpipe) > 0 )    /* check for commands */
  393.             {    read(cronpipe,&cmd,1);
  394.                 switch(cmd)
  395.                 {    case EXTERNBUILD: /* log only external cmds */
  396.                         LogMsg(PROGNAME,getpid(),"REREAD : crontabs rebuilded");
  397.                     case INTERNBUILD:
  398.                         jobcount = RebuildTables(joblist);
  399.                         break;
  400.                     default:
  401.                         LogMsg(PROGNAME,getpid(),"Unknown command on pipe");
  402.                 }                        
  403.             }
  404.  
  405.             if(jobcount > 0)
  406.                 runjobs=CalcStartTimes(joblist,jobcount,starttime);
  407. #ifdef DEBUG
  408.             else
  409.                 Debug("Currently no jobs in table\n");
  410. #endif
  411.             interval = starttime - currenttime;
  412.             cronsleep(interval < SLEEPTIME ? interval : SLEEPTIME);
  413.         }
  414.         while((starttime - time(¤ttime)) > 1L);
  415.  
  416.         HandleExits(ajoblist);    /* from previous run(s) */
  417.         if(runjobs > 0)
  418.             DoJobs(joblist,jobcount,ajoblist);
  419.     }
  420. }